import fs from 'node:fs'
import path from 'node:path'
import { CurdTemplate, DbHolder } from "./db/index.ts"
import { HttpApplication } from "./web/index.ts"
import { bodyParser, serve, Router } from './web/middleware/index.ts'
import { IHttpContext } from "types"

const mysql = {
  // 服务器地址
  host: '127.0.0.1',
  // 端口
  port: 3306,
  // 数据库编码默认采用utf8/utf8mb4
  charset: 'utf8mb4',
  // 数据库名
  database: 'beers',
  // 用户名
  user: 'root',
  // 密码
  password: '123456',
  connectTimeout: 10000,
  supportBigNumbers: true,
  bigNumberStrings: true,
  // 同时使用多条查询语句
  multipleStatements: true,
  // https://stackoverflow.com/questions/71949351/how-to-use-named-parameters-in-node-mysql2-prepared-statements
  namedPlaceholders: true,
  waitForConnections: true,
  connectionLimit: 16,
  queueLimit: 0
}

const app = new HttpApplication()
const router: any = new Router()
const port = 3001

await DbHolder.getInstance().initDb(mysql)
//
const uploadDir = path.join(process.cwd(), 'public/upload/')
console.log(`uploadDir = ${uploadDir}`)
app.use(bodyParser({
  multipart: true, // 支持文件上传
  encoding: 'gzip',
  formidable: {
    uploadDir: uploadDir, // 设置文件上传目录
    keepExtensions: true,    // 保持文件的后缀
    multipart: true,    // 是否支持多文件上传
    maxFields: 32, // 文件的数量
    maxFieldsSize: 2 * 1024 * 1024, // 文件上传大小
    onFileBegin: (name: string, file: string) => { // 文件上传前的一些设置操作
      // console.log(`name: ${name}`);
      // console.log(file);
    },
  }
}))

// 静态资源
app.use(serve('public'))

app.use(async (ctx, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  console.log(`${ctx.req.method} ${ctx.req.url} - ${ms}ms`)
})

// dao 层
class UserDao {

  static instance: UserDao

  static getInstance() {
    if (!UserDao.instance) {
      UserDao.instance = new UserDao()
    }
    return UserDao.instance
  }

  static ii = 1
  curdTemplate: CurdTemplate

  constructor() {
    this.curdTemplate = DbHolder.getInstance().getCurdTemplate()
  }

  async findUser() {
    const [rows, fields] = await this.curdTemplate.query(`SELECT * FROM users`)
    console.log(rows)
    return rows
  }

  async addUser() {
    const id = Date.now() % 100000000
    const res = await this.curdTemplate.execute(`insert into users values(${id + ii++},'${id}',CURRENT_TIMESTAMP)`)
    return res
  }

  async addUser1() {
    const id = Date.now() % 100000000
    const res = await this.curdTemplate.execute(`insert into users values(${id + ii++},'${id}','2020-01-01')`)
    return res
  }

  async addUser2() {
    const id = Date.now() % 100000000
    const res = await this.curdTemplate.execute(`insert into users values(${id + ii++},'${id}','2022-10-19')`)
    return res
  }
}

// dao 层
class UserService {

  static instance: UserService

  static getInstance() {
    if (!UserService.instance) {
      UserService.instance = new UserService()
    }
    return UserService.instance
  }

  userDao: UserDao

  constructor() {
    this.userDao = UserDao.getInstance()
  }

  async findUser() {
    return await this.userDao.findUser()
  }

  async addUser() {
    return await DbHolder.getInstance().transaction(async () => {
      return await this.userDao.addUser()
    })
  }

  async addUsers() {
    return await DbHolder.getInstance().transaction(async () => {
      const res1 = await this.addUser()
      const res2 = await this.userDao.addUser()
      const res3 = await this.addUser1()
      const res4 = await this.userDao.addUser2()
      return [res1, res2, res3, res4]
    })
  }

  async addUser1() {
    const res = await this.userDao.addUser1()
    return res
  }
}

router.get('/', async (ctx: IHttpContext) => {
  ctx.body = '~~~~~~~~~ Hello Summer 中国 ~~~~~~~~~'
})

router.get('/api/users', async (ctx: IHttpContext) => {
  const resData = [
    {
      id: 1,
      name: '小明',
      age: 18,
    },
    {
      id: 2,
      name: '小红',
      age: 19,
    },
  ]

  ctx.body = resData
})

router.get('/user/find', async (ctx: IHttpContext) => {
  const curdTemplate = DbHolder.getInstance().getCurdTemplate()
  const [rows, fields] = await curdTemplate.query(`SELECT * FROM users`)
  console.log(rows)
  ctx.body = rows
})

let ii = 1
router.get('/user/add', async (ctx: IHttpContext) => {
  const dbHolder = DbHolder.getInstance()
  const curdTemplate = dbHolder.getCurdTemplate()
  await dbHolder.transaction(async () => {
    const id = Date.now() % 100000000
    await curdTemplate.execute(`insert into users values(${id + ii++},'${id}',CURRENT_TIMESTAMP)`)
    await curdTemplate.execute(`insert into users values(${id + ii++},'${id}',CURRENT_TIMESTAMP)`)
    await curdTemplate.execute(`insert into users values(${id + ii++},'${id}',CURRENT_TIMESTAMP)`)
    const res = await curdTemplate.execute(`insert into users values(${id + ii++},'${id}',CURRENT_TIMESTAMP)`)
    ctx.body = { code: 0, data: res }
  })
})

router.get('/user/find1', async (ctx: IHttpContext) => {
  const rows = await UserService.getInstance().findUser()
  console.log(rows)
  ctx.body = rows
})

router.get('/user/add1', async (ctx: IHttpContext) => {
  const res = await UserService.getInstance().addUser()
  ctx.body = { code: 0, data: res }
})

router.get('/user/add1s', async (ctx: IHttpContext) => {
  const res = await UserService.getInstance().addUsers()
  ctx.body = { code: 0, data: JSON.stringify(res, null, 4) }
})

router.post('/api/users', async (ctx: IHttpContext) => {
  // 使用了koa-bodyparser才能从ctx.request拿到body
  const postData = ctx.request.body

  // 使用fs.promises模块下的方法，返回值是promises
  const filename = path.resolve(process.cwd(), 'public/data/db.txt')
  await fs.promises.appendFile(filename, JSON.stringify(postData))

  ctx.body = postData
})

router.post('/upload', async (ctx: IHttpContext) => {
  console.log(`ctx.request.files = `, ctx.request.files)
  console.log(`ctx.request.body = `, ctx.request.body)
  ctx.body = ctx.request.files
})

app.use(router.routes())

app.listen(port, () => {
  console.log(`Summer Server is running on http://127.0.0.1:${port}/`)
})
